// states
var NONE = 0;
var PAN = 1;
var ZOOM_IN = 2;
var ZOOM_OUT = 3;
var ZOOM = 4;
var SPLIT_H = 10;
var SPLIT_V = 11;
var SPLIT = 12;
var MERGE = 13;
var SWAP = 14;


var KEY_LEFT = 37;
var KEY_UP = 38;
var KEY_RIGHT = 39;
var KEY_DOWN = 40;
var KEY_PGUP = 33;
var KEY_PGDOWN = 34;

var MIN_UNIT_SIZE = 3;
var _controller = null;

// roles
var PLAY_WHITE = 1;
var PLAY_BLACK = 2;
var WATCH = 3;
var UNKNOWN = 4;

function Validator() {
  this.enabled = true;
}

Validator.prototype.enable = function(e) {
  this.enabled = e;
}

Validator.prototype.isEnabled = function() {
  return this.enabled;
}

function ListOfMoves(elName) {
  this.elName = "#" + elName;
  this.moveIdViewed = 0;
  this.moveId = 0;
  this.refresh();
}

ListOfMoves.prototype.submitMove = function(moveId) {
  this.moveId = moveId - 1;
  this.moveIdViewed = 0;
  this.refresh();
}

ListOfMoves.prototype.undoMove = function(moveId) {
  this.moveId = moveId - 1;
  this.moveIdViewed = 0;
  this.refresh();
}

ListOfMoves.prototype.setMoveSelected = function(id) {
  this.moveIdViewed = id;
  this.refresh();
}

ListOfMoves.prototype.getViewedMoveId = function() {
  return this.moveIdViewed;
}

ListOfMoves.prototype.refresh = function() {
  var i = 1;

  var template = $('#moveHistoryTemplate').html();

  var tag = "#moveHistory ol";
  $(tag).html("");

  for (var j = this.moveId+1; j > 0; j--) {
    var color =  (j % 2) ? BLACK: WHITE;
    var additionalClass = "";
    if (this.moveIdViewed == j || (this.moveIdViewed == 0 && j == this.moveId+1)) {
      additionalClass = "moveViewed";
    }
    var move = $(template).bindTo({'color':COLOR_TEXT[color], 'additionalClass': additionalClass, 'move':j},{'fill':false});
    $(move).appendTo(tag);
  }
}


ListOfMoves.prototype.isViewing = function() {
  return this.moveIdViewed > 0;
}


function ListOfParticipants(elName) {
  this.elName = "#" + elName;
  this.participants = [];
}

ListOfParticipants.prototype.update = function(users, whitePlayer, blackPlayer) {
  var participants = [];
  for (var i = 0; i < users.length; i++) {
    participants[users[i].email] = users[i];
  }
  // todo: replace to style sheets
  var status = this.getUserStatus(participants[blackPlayer]);
  var rating = "n/a";
  if (participants[blackPlayer] != null) {
    rating = participants[blackPlayer].rating;
  }
  this.setPlayer("black", blackPlayer, rating, status);
  status = this.getUserStatus(participants[whitePlayer]);
  rating = "n/a";
  if (participants[whitePlayer] != null) {
    rating = participants[whitePlayer].rating;
  }
  this.setPlayer("white", whitePlayer, rating, status);

  var tag = $('#viewers ul');
  tag.html(""); // clear list
  for (var i = 0; i < users.length; i++) {
    if (users[i].email != whitePlayer && users[i].email != blackPlayer) {
      var status = this.getUserStatus(users[i]);
      this.setViewer(tag, users[i].email, users[i].rating, status);
    }
  }
}

ListOfParticipants.prototype.setPlayer = function(color, name, rating, status) {
  var template = $('#playerTemplate').html();

  var player = $(template).bindTo({name:name, rating:rating, status:status},{'fill':false});
  var tag = "#activePlayer." + color + " .player";
  $(tag).html("");
  $(player).appendTo(tag);
}

ListOfParticipants.prototype.setViewer = function (tag, name, rating, status) {
  var template = $('#viewerTemplate').html();

  var viewer = $(template).bindTo({name:name, rating:rating, status:status},{'fill':false});
  $(viewer).appendTo(tag);
}

ListOfParticipants.prototype.getUserStatus = function(user) {
      var status = "Loggedoff";
      if (user != null && user.active != null) {
        if (user.active == "false") {
          status = "Idle";
        } else {
          status = "Active";
        }
      }
      return status;
}

function Controller(canvasName, remotePlay) {
    this.board = null;
    this.render = null;
    this.view = null;
    this.state = NONE;
	this.canvasName = canvasName;
	this.validator = null;
	this.remotePlay = remotePlay;
	if (this.remotePlay == undefined) {
	  this.remotePlay = null;
	}
	this.playerColor = NULL_COLOR;
	this.whitePlayer = "";
	this.blackPlayer = "";
    this.alertTimerId = 0;
    this.title = "Crumble";
    document.title = this.title;
    this.tutorial = new Tutorial();
    this.tutorial.enable(true);
	_controller = this;
}

Controller.prototype.init = function(sizeController) {
  assert(sizeController);
  var elName = "#" + this.canvasName;
  this.elName = elName;
  this.view = null;
  this.sizeController = sizeController;
  $(elName).click(this.click);
  $(elName).dblclick(this.dblclick);
  $(elName).bind("contextmenu", this.contextmenu);
  $(elName).keydown(this.keydown);
  $(document).keydown(this.keydown);
  $(elName).keyup(this.keyup);
  $(elName).mousedown(this.mousedown);
  $(elName).mouseup(this.mouseup);
  $(elName).mousemove(this.mousemove);
  $(elName).mouseout(this.mouseout);
  $(elName).mouseover(this.mouseover);
  $(elName).scroll(this.scroll);
  $(elName).scrollLeft(this.scrollLeft);
  $(elName).scrollTop(this.scrollTop);
  this.validator = new Validator();
  this.turnValidation();  // initialize validator
  if (this.remotePlay == null) {
    this.newGame(UNKNOWN);
  } else {
    this.remotePlay.init();
	this.showMessage("Select your color and click 'new game'");
    this.remotePlay.sendNewGameRequest(""); // see if the game was already started, then watch it
  }
}

Controller.prototype.setWhitePlayer = function(email) {
  this.whitePlayer = email;
}

Controller.prototype.setBlackPlayer = function(email) {
  this.blackPlayer = email;
}

Controller.prototype.getBoard = function() {
  return this.game.getBoard();
}

Controller.prototype._getPiece = function(e) {
}

Controller.prototype.getPiece = function(x, y) {
  var realX = 1.0 * (x - this.view.boardStartX) / this.view.unitSize;
  var realY = 1.0 * (y - this.view.boardStartY) / this.view.unitSize;
  return this.getBoard().getPiece(realX, realY);
}

Controller.prototype.getX = function(e) {
  return e.pageX - e.currentTarget.offsetLeft;
}

Controller.prototype.getY = function(e) {
  return e.pageY - e.currentTarget.offsetTop;
}

Controller.prototype.getClosestSide = function(piece, x, y) {
  var realX = 1.0 * (x - this.view.boardStartX) / this.view.unitSize;
  var realY = 1.0 * (y - this.view.boardStartY) / this.view.unitSize;
  var middleX = piece.start[HORIZONTAL].add(piece.x.half()).float();
  var middleY = piece.start[VERTICAL].add(piece.y.half()).float();
  if (Math.abs(realX - middleX) > Math.abs(realY - middleY)) {
    return (realX > middleX)?RIGHT:LEFT;
  } else {
    return (realY > middleY)?BOTTOM:TOP;
  }
}



Controller.prototype.redraw = function() {
  this.render.draw(this.getBoard());
  this.showMessage(this.game.getMessage());
  this.showMaterial();
}

Controller.prototype.showMessage = function(message) {
  if(message =="") {
    message = "OK";
  }
  $("#message").html(message);
}

Controller.prototype.showMaterial = function() {
  var materialBlack = this.game.getBoard().material(BLACK);
  var materialWhite = this.game.getBoard().x * this.game.getBoard().y - materialBlack;
  $("#material").html("Material: Black: " + materialBlack + "; White: " + materialWhite);
}

Controller.prototype.showWhoseMove = function() {
  if (this.game.gameOver) {
	$("#whoseMove").html("" + this.game.getMoveId() + ": " + "<b>" + COLOR_TEXT[this.game.getWinningColor()] + "</b>" + " won");
  } else {
    $("#whoseMove").html("" + this.game.getMoveId() + ": " + "<b>" + COLOR_TEXT[this.game.getCurrentColor()] + "</b>" + " to move");
  }
}

Controller.prototype.checkValid = function() {
  this.getBoard()._assertValid();
}

Controller.prototype.updateUsers = function(users) {
  if (!this.listOfParticipants) {
    this.listOfParticipants = new ListOfParticipants("participants");
  }
  this.listOfParticipants.update(users, this.whitePlayer, this.blackPlayer);
}

Controller.prototype.cancelMove = function() {
  if (_controller.remotePlay != null && _controller.playerColor != _controller.game.getCurrentColor()) {
    _controller.showMessage("Can not cancel the other player's move");
    return;
  }

  this.game.cancelMove();
  this.redraw();
  this.showWhoseMove();
}

Controller.prototype.leaveTable = function() {
  this.remotePlay.leaveTable();//OPPOSITE_COLOR[this.playerColor]);
}

Controller.prototype.makeRemoteMove = function(move) {
  assert(this.remotePlay != null && this.playerColor != this.game.getCurrentColor()); //XXX && this.game.getMoveId() > 1 || this.game.getMoveId() == 1);
  var msg = "New Move!";
  this.alertTimerId = setInterval(function() {
     document.title = document.title == msg ? _controller.title : msg;
  }, 1000);
  this.recreateMove(move);
}

Controller.prototype.recreateMove = function(move) {
  this.game.makeMoveFromRepresentation(move);
  this.redraw();
  this.showWhoseMove();
}


Controller.prototype.submitMove = function() {
  var success = this.game.submitMove();
  if (!success) {
    this.redraw();
    return;
  }
  // for remote play, submit move is called to complete opponents move - (player color does not match)
  // as well as for the self-move. In the latter case we need to send self move to the server
  // in the former case we just visually complete the move but don't do anything with the server
  if (this.remotePlay != null
      && this.playerColor == this.game.getLastPlayedColor()
      && this.moveToCatchup < this.game.getMoveId()-1) {
    this.remotePlay.sendMove(this.game.getLastMoveRepresentation(), this.game.getWinningColor());
  }
  this.listOfMoves.submitMove(this.game.moveId);
  this.redraw();
  this.showWhoseMove();
}

Controller.prototype.undo = function() {
  if (this.remotePlay != null) {
    this.showMessage("Undo is not supported for remote game");
    return;
  }
  assert(this.remotePlay == null);
  this.game.undoLastMove();
  this.listOfMoves.undoMove(this.game.moveId);
  this.redraw();
  this.showWhoseMove();
}

Controller.prototype.newGame = function(role, moveToCatchup) {
  this.moveToCatchup = moveToCatchup;
  if (role == UNKNOWN && this.remotePlay != null && this.game && !this.game.gameOver && this.game.getMoveId() > 1) {
    this.showMessage("Current game is not over. Can not start a new game");
    return;
  }
  if (this.remotePlay != null && role == PLAY_WHITE) {
     this.playerColor = WHITE;
  } else if (this.remotePlay != null && role == PLAY_BLACK) {
     this.playerColor = BLACK;
  } else if (this.remotePlay != null) {
    this.playerColor = NULL_COLOR;
  }

  if (this.remotePlay == null || this.remotePlay != null && role != UNKNOWN) {
	  var whiteComp = false;
	  var blackComp = false;
	  if (this.remotePlay == null) {
	    whiteComp = $("#whitePlayerComp")[0].checked;
	    blackComp = $("#blackPlayerComp")[0].checked;
	  }
	  this.game = new Game(6, 6, this.validator, whiteComp, blackComp);
	  if (this.remotePlay == null) {
	    this.setDemo();
	  }
	  var unitSize = $(this.elName).height() / this.getBoard().y;
	  if (this.view == null) {
		  this.view = new View(unitSize, 0, 0, $(this.elName).width(), $(this.elName).height())
		  this.out = new RealOutput(this.view, this.canvasName, $("#gameSkins").val());
		  this.overlay = new Overlay(this.out);
		  this.render = new Render(this.out, this.view, this.overlay);
	      this.stateManager = new StateManager(this, this.overlay);
	  }
		  /*
		  if (_controller.sizeController == null) {
		    setTimeout('_controller.onresize()',20);
            this.onresize();
          } else {
            setTimeout('_controller.sizeController.onresize()', 20);
            _controller.sizeController.onresize();
          }*/
      this.redraw();
	  this.showWhoseMove();
	  this.listOfMoves = new ListOfMoves("moveHistory");
	  //if (role == PLAY_WHITE || role == WATCH || _controller.moveToCatchup > 1) {
	  if (this.remotePlay != null) {
	    this.remotePlay.startListeningForNextMove(1);
	  }
	  if (this.remotePlay != null ) {
	    $("#newGameWhite").toggle(false);
	    $("#newGameBlack").toggle(false);
	  }
  }
  if (this.remotePlay != null && role == UNKNOWN) {
    if (!$("#desiredBlack")[0].checked) {
      desiredColor = COLOR_TEXT[WHITE];
    } else {
      desiredColor = COLOR_TEXT[BLACK];
    }
    this.showMessage("Sending new game request for " + desiredColor + "... Please wait.");
    this.remotePlay.sendNewGameRequest(desiredColor);
  }
}

Controller.prototype.requestNewGame = function(color) {
  $("#desiredBlack")[0].checked = (color == BLACK);
  this.newGame(UNKNOWN);
}

Controller.prototype.snapView  = function() {
  if (this.view == null) {
    return;
  }
  this.view.unitSize = $(this.elName).height() / this.getBoard().y;
  this.view.boardStartX = 0;
  this.view.boardStartY = 0;
  this.view.windowWidth = $(this.elName).width();
  this.view.windowHeight = $(this.elName).height();
  this.redraw();
}

Controller.prototype.turnValidation = function() {
  if (this.remotePlay != null) {
    this.validator.enable(true);
  } else {
	  this.validator.enable($("#validator")[0].checked);
	  this.showMessage((this.validator.isEnabled())?"Enabling validation...":"Validation is off");
  }
}

Controller.prototype.setDemo = function() {
  assert(this.remotePlay == null);
  var demo = $("#demo")[0].checked;
  if (demo) {
    $("#blackPlayerComp")[0].checked = true;
    $("#whitePlayerComp")[0].checked = true;

    setTimeout("_controller.game.setDemoMode(true)", 100);
  }
}

Controller.prototype.setBlackPlayerComp = function() {
  assert(this.remotePlay == null);
  this.game.setPlayerComp(BLACK, $("#blackPlayerComp")[0].checked);
  this.redraw();
}

Controller.prototype.setWhitePlayerComp = function() {
  assert(this.remotePlay == null);
  this.game.setPlayerComp(WHITE, $("#whitePlayerComp")[0].checked);
  this.redraw();
}

Controller.prototype.click = function(e) {
   if (_controller.listOfMoves.isViewing()) {
    _controller.showMessage("View only mode");
    return;
  }
  if (_controller.remotePlay != null &&
     (_controller.playerColor != _controller.game.getCurrentColor())
     || (_controller.moveToCatchup > _controller.game.getMoveId())) {
    _controller.showMessage("Please wait for the other player to make a move");
    return;
  }
  _controller.stateManager.click(e);
}

Controller.prototype.dblclick= function(e) {
	_controller.stateManager.dblclick(e);
}

Controller.prototype.contextmenu = function(e) {
  _controller.tutorial.getPieceActions(_controller.getPiece(e), _controller.game);
  return false;
}

Controller.prototype.keydown= function(e) {
}

Controller.prototype.keypress= function(e) {
}

Controller.prototype.keyup= function(e) {
}

Controller.prototype.mousedown= function(e) {
  _controller.stateManager.mousedown(e);
}


Controller.prototype.mouseup = function(e) {
  if (_controller.remotePlay != null && _controller.playerColor != _controller.game.getCurrentColor()) {
    _controller.showMessage("Please wait for the other player to make a move");
    return;
  }
  if (_controller.listOfMoves.isViewing()) {
    _controller.showMessage("View only mode");
    return;
  }
  _controller.stateManager.mouseup(e);
}

Controller.prototype.mousemove = function(e) {
  if (_controller.remotePlay) {
    _controller.remotePlay.remoteUser.onActivityDetected(false);
  }
  if (_controller.alertTimerId) {
    clearInterval(_controller.alertTimerId);
    _controller.alertTimerId = 0;
    document.title = _controller.title;
  }
  _controller.stateManager.mousemove(e);
}

Controller.prototype.mouseout= function(e) {
  _controller.stateManager.mouseout(e);
}

Controller.prototype.mouseover = function(e) {
  _controller.stateManager.mouseover(e);
}

Controller.prototype.scroll= function(e) {
}

Controller.prototype.scrollLeft= function(e) {
}

Controller.prototype.scrollTop= function(e) {
}

Controller.prototype.onMoveSelected = function(v) {
  if (this.game.getMoveId() == v) {
    v = 0;
  }
  this.listOfMoves.setMoveSelected(v);
  this.game.setMoveToView(v);
  this.redraw();
}

Controller.prototype.onButton = function(state) {
  this.stateManager.resetState(state);
}


Controller.prototype.skinChanged = function(newSkin) {
  if (this.out.setSkin(newSkin)) {
    this.redraw();
  }
}

Controller.prototype.colorSchemeChanged = function(newScheme) {
  $("body").removeClass();
  $("body").addClass(newScheme);
}

Controller.prototype.setCursor = function(cur) {
  if (cur != '') {
    cur = "url(\"/static/cursors/" + cur + ".cur\"), pointer";
  } else {
    cur = 'default';
  }
  $(this.elName).css('cursor', cur);
}